#pragma once

// Copyright (c) 2010 Manuel Peinado Gallego <manuel.peinado@gmail.com>
// Distributed under the MIT license

// ShaderManager.h : interface of the ShaderManager class
//

#include "SmartEnum.h"
#include "RenderListener.h"
#include "Ref.h"
class ShaderProgram;
class ShaderEvent;
class ShaderListener;



// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// RenderListener to be notified of beginRender events so that we can
// update the "time" uniform variable of timed shaders


class ShaderManager : boost::noncopyable,
                      public RenderListener,
                      public ContinuousRenderRequester
{
    typedef std::set<ref<ShaderProgram> > ShaderSet;

public:

    // Register flags
    enum
    {
        IsTimedShader = 1
    };

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    ShaderManager();
    ~ShaderManager();

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    void registerShader(ref<ShaderProgram> prog, int flags=0);

    // - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

    // Deletes all shaders
    void clear();

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    ShaderProgram* getShaderById(int id) const;
    ShaderProgram* getShaderByName(const std::wstring& name) const;
    
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    std::wstring generateNameFromPrefix(const std::wstring& prefix) const;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    typedef ShaderSet::const_iterator ShaderIt;
    ShaderIt beginShaders() const;
    ShaderIt endShaders() const;
    size_t getShaderCount() const;

    // - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

    // This will be called by the shaders that are registered with this 
    // manager when one of their parameters change. The manager, in turn,
    // will notifiy all its listeners

    void notifyShaderEvent(ShaderEvent&);

    // - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
    // Management of the listener set

    void addShaderListener(ShaderListener* listener);
    void removeShaderListener(ShaderListener* listener);

    // - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
    
    // Updates the "time" uniform variable of timed shaders
    void onBeginRender(View3d*) override;

    // - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

    bool needsContinuousRender(View3d*);

    // - - -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 

    // Loads a vertex shader from disk, a fragment shader, and registers
    // the program resulting from combining them
    ref<ShaderProgram> simpleLoad(const std::string& fileName, int flags=0);

private:

    // We use set<> to avoid duplicates
    typedef std::set<ShaderListener*>::iterator ListenerIt;
    std::set<ShaderListener*> m_listeners;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    ShaderSet m_shaders;

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    std::map<ShaderProgram*, int> m_timeUniformVarLocations;
    std::set<ShaderProgram*> m_timedShaders;
    float m_time;

};

